// 回射服务器基本模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/unistd.h>
#define BACKLOG 20
#define PORT 8888
#define BUFFSIZE 4096
#define MAX_EVENTS 20
void upper(char buffer[], int len);
void processClient(int clientFd, int epollFd);
int main(void)
{
    int serverFd;
    serverFd = socket(AF_INET, SOCK_STREAM, 0);
    if (serverFd > 0)
    {
        printf("socket %d created successed.\n", serverFd);
    }
    else
    {
        printf("socket created failed.\n");
        exit(-1);
    }
    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(PORT);
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    int error = bind(serverFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    if (error == 0)
    {
        printf("bind successed.\n");
    }
    else
    {
        printf("bind failed.\n");
        exit(-1);
    }
    error = listen(serverFd, BACKLOG);
    if (error == 0)
    {
        printf("listen successed.\n");
    }
    else
    {
        printf("listen failed.\n");
        exit(-1);
    }

    struct epoll_event ev, events[MAX_EVENTS];
    int epollFd;
    epollFd = epoll_create1(0);
    if (epollFd > 0)
    {
        printf("epoll created successed.\n");
    }
    else
    {
        printf("epoll created failed.\n");
        exit(-1);
    }
    ev.events = EPOLLIN;
    ev.data.fd = serverFd;
    int err = epoll_ctl(epollFd, EPOLL_CTL_ADD, serverFd, &ev);
    if (err == 0)
    {
        printf("epoll add sucessed.\n");
    }
    else
    {
        printf("epoll add failed.\n");
        exit(EXIT_FAILURE);
    }
    int nfds;
    while (1)
    {
        nfds = epoll_wait(epollFd, events, MAX_EVENTS, -1);
        printf("get %d requests\n", nfds);
        for (int i = 0; i < nfds; ++i)
        {
            printf("events[i].data.fd = %d\n", events[i].data.fd);
            if (events[i].data.fd == serverFd)  // 处理监听套接字
            {
                struct sockaddr_in clientAddr;
                socklen_t clientAddrLen = sizeof clientAddr;
                int clientFd = accept(serverFd, (struct sockaddr*)&clientAddr, &clientAddrLen);
                if (clientFd > 0)
                {
                    printf("new connection:%d\n", clientFd);
                    printf("port: %d\n", clientAddr.sin_port);
                    char* ip = inet_ntoa(clientAddr.sin_addr);
                    printf("ipv4: %s\n", ip);
                    ev.events = EPOLLIN | EPOLLET;  // 设置边沿触发
                    ev.data.fd = clientFd;
                    int err = epoll_ctl(epollFd, EPOLL_CTL_ADD, clientFd, &ev);
                    if (err == 0)
                    {
                        printf("epoll add client sucessed.\n");
                    }
                    else
                    {
                        printf("epoll add client failed.\n");
                        exit(EXIT_FAILURE);
                    }
                }
                else
                {
                    printf("accept failed.\n");
                    exit(-1);
                }
            }
            else    // 处理客户端套接字
            {
                // pause();
                printf("deal clientFd:%d\n", events[i].data.fd);
                processClient(events[i].data.fd, epollFd);
            }
        }
    }
    
    return 0;
}
void upper(char buffer[BUFFSIZE], int len)
{
    for (int i = 0; i < len; ++i)
    {
        if (buffer[i] < 97 || buffer[i] > 122)
        {
            continue;
        }
        buffer[i] -= 32;
    }
}
void processClient(int clientFd, int epollFd)
{
    char buffer[BUFFSIZE] = {0};
    int n = recv(clientFd, buffer, sizeof buffer, 0);
    if (n > 0)
    {
        printf("%s\n", buffer);
        upper(buffer, strlen(buffer));
        send(clientFd, buffer, strlen(buffer)+1, 0);
    }
    else
    {
        printf("socket %d break.\n", clientFd);
        epoll_ctl(epollFd, EPOLL_CTL_DEL, clientFd, NULL);
        close(clientFd);
    }

}